﻿using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.Time;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using GHI.Premium.Net;

using GadgeteerLibrary;

namespace UseCaseTwoHost
{
    public partial class Program
    {
        private GT.Timer connectTimer;
        private GT.Timer heartBeatTimer;
        private RESTClient restClient;
        private static bool timeSynchronized = false;
        private bool hasNexusConnected = false;
        private bool hasSeeedConnected = false;

        private const byte NOTE_SIZE = 2;
        private const int CONNECT_INTERVAL = 600000;
        private const int HEARTBEAT_INTERVAL = 300000;
        private const int MEASUREMENT_INTERVAL = 4000;
        private const int SEND_INTERVAL = 8000;
        private const short FAULT_TOLERANCE = 60;
        private const string TARGET_SSID = "Connectify-Zheng";
        private const string PASSWORD = "gadg3t33r";

        Program()
        {
            string apiKey = "72b2480d-6ff2-436f-be82-6560138ad24e";
            Device device = new Device(2065, "BluetoothHost");
            ArrayList sensors = new ArrayList();
            sensors.Add(new Sensor("GPS8", SensorType.gps, "Seeed", MEASUREMENT_INTERVAL, SEND_INTERVAL, FAULT_TOLERANCE));
            restClient = new RESTClient(apiKey, device, sensors);
        }

        void ProgramStarted()
        {
            Debug.Print("Program Started");
            this.InitializeNetwork();
            this.InitializeBluetoothButton();
        }

        void InitializeNetwork()
        {
            connectTimer = new GT.Timer(CONNECT_INTERVAL);
            connectTimer.Tick += connectTimer_Tick;

            if (!wifi_RS21.Interface.IsOpen)
            {
                wifi_RS21.Interface.Open();
            }

            if (!wifi_RS21.Interface.NetworkInterface.IsDhcpEnabled)
            {
                wifi_RS21.Interface.NetworkInterface.EnableDhcp();
            }
            else
            {
                wifi_RS21.Interface.NetworkInterface.RenewDhcpLease();
            }

            // use the router's DHCP server to set my network info
            wifi_RS21.UseDHCP();

            // setup events
            wifi_RS21.NetworkDown += wifi_NetworkDown;
            wifi_RS21.NetworkUp += wifi_NetworkUp;
            wifi_RS21.Interface.NetworkAddressChanged += Interface_NetworkAddressChanged;
            wifi_RS21.Interface.WirelessConnectivityChanged += Interface_WirelessConnectivityChanged;

            NetworkInterfaceExtension.AssignNetworkingStackTo(wifi_RS21.Interface);

            try
            {
                if (this.WifiConnect())
                {
                    Thread.Sleep(2000);
                    Debug.Print("Up: " + wifi_RS21.IsNetworkUp.ToString());
                    Debug.Print("IP Address: " + wifi_RS21.Interface.NetworkInterface.IPAddress);
                    // after connecting, set local time, register the sensors and initiate the heart beat message
                    this.SetLocalTime();
                    this.RegisterSensor();
                    this.HeartBeatMessage();
                }
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        void HeartBeatMessage()
        {
            heartBeatTimer = new GT.Timer(HEARTBEAT_INTERVAL);
            heartBeatTimer.Tick += heartBeatTimer_Tick;
            heartBeatTimer.Start();
        }

        void heartBeatTimer_Tick(GT.Timer timer)
        {
            if (wifi_RS21.IsNetworkConnected)
            {
                if (restClient.HeartBeatMessage())
                {
                    Debug.Print("Heart beat notification for Device "
                        + restClient.Device.Name + " successful");
                }
                else
                {
                    Debug.Print("Heart beat notification for Device "
                        + restClient.Device.Name + " failed");
                }
            }
            else
            {
                Debug.Print("Please check the network connection.");
            }
        }

        void connectTimer_Tick(GT.Timer timer)
        {
            if (this.WifiConnect())
            {
                Debug.Print("Reconnection successful");
            }
            else
            {
                Debug.Print("Reconnection failed");
            }
        }

        bool WifiConnect()
        {
            try
            {
                if (wifi_RS21.IsNetworkConnected)
                {
                    wifi_RS21.Interface.Disconnect();
                    Thread.Sleep(2000);
                }

                // to use hold the network to connect to
                WiFiNetworkInfo[] targetNetwork = wifi_RS21.Interface.Scan(TARGET_SSID);

                // connect to the specified network
                if (targetNetwork != null && targetNetwork.Length > 0)
                {
                    WiFiNetworkInfo target = targetNetwork[0];

                    Debug.Print("****" + target.SSID + "****");
                    Debug.Print("ChannelNumber = " + target.ChannelNumber);
                    Debug.Print("NetworkType = " + target.networkType);
                    Debug.Print("PhysicalAddress = " + HttpUtility.GetMACAddress(target.PhysicalAddress));
                    Debug.Print("RSSI = " + target.RSSI);
                    Debug.Print("SecMode = " + target.SecMode);

                    wifi_RS21.Interface.Join(target, PASSWORD);

                    Debug.Print("Network joined");
                    return true;
                }
                else
                {
                    Debug.Print(TARGET_SSID + " wireless network not found");
                    return false;
                }
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
                return false;
            }
        }

        void SetLocalTime()
        {
            TimeService.SystemTimeChanged += TimeService_SystemTimeChanged;
            TimeService.TimeSyncFailed += TimeService_TimeSyncFailed;
            TimeService.Settings = NtpClient.TimeServiceSetting();
            TimeService.Start();
        }

        void RegisterSensor()
        {
            while (!restClient.RegisterSensor())
            {
                Debug.Print("Registration failed");
            }

            foreach (Object o in restClient.Sensors)
            {
                Sensor s = (Sensor)o;
                Debug.Print("Sensor ID for " + s.Name + ": " + s.Id);
            }

            Debug.Print("Registration successful");
        }

        void Interface_WirelessConnectivityChanged(object sender, WiFiRS9110.WirelessConnectivityEventArgs e)
        {
            if (e.IsConnected)
            {
                Debug.Print("Network is connected");
                if (connectTimer.IsRunning)
                {
                    connectTimer.Stop();
                }
            }
            else
            {
                Debug.Print("Network is disconnected");
                if (!connectTimer.IsRunning)
                {
                    connectTimer.Start();
                }
            }
        }

        void TimeService_TimeSyncFailed(object sender, TimeSyncFailedEventArgs e)
        {
            Debug.Print("Error synchronizing system time with NTP server: " + e.ErrorCode);
        }

        void TimeService_SystemTimeChanged(object sender, SystemTimeChangedEventArgs e)
        {
            Debug.Print("Network time received: " + DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
            if (!timeSynchronized)
            {
                timeSynchronized = true;
            }
        }

        // handle the network changed events
        void wifi_NetworkDown(GTM.Module.NetworkModule sender,
            GT.Modules.Module.NetworkModule.NetworkState state)
        {
            if (state == GTM.Module.NetworkModule.NetworkState.Down)
            {
                Debug.Print("Network Down event; state = Down");
            }
            else
            {
                Debug.Print("Network Down event; state = Up");
            }
        }

        void wifi_NetworkUp(GTM.Module.NetworkModule sender,
            GT.Modules.Module.NetworkModule.NetworkState state)
        {
            if (state == GTM.Module.NetworkModule.NetworkState.Up)
            {
                Debug.Print("Network Up event; state = Up");
            }
            else
            {
                Debug.Print("Network Up event; state = Down");
            }
        }

        void Interface_NetworkAddressChanged(object sender, EventArgs e)
        {
            if (wifi_RS21.IsNetworkConnected)
            {
                if (restClient.NotifyIPAddress(wifi_RS21.Interface.NetworkInterface.IPAddress))
                {
                    Debug.Print("Notification of IP Address changing for Device "
                        + restClient.Device.Name + " successful");
                }
                else
                {
                    Debug.Print("Notification of IP Address changing for Device "
                        + restClient.Device.Name + " failed");
                }
            }
            else
            {
                Debug.Print("Please check the network connection.");
            }
        }

        void InitializeBluetoothButton()
        {
            button1.ButtonPressed += button1_ButtonPressed;
            bluetooth1.BluetoothStateChanged += bluetooth1_BluetoothStateChanged;
            bluetooth1.DataReceived += bluetooth1_DataReceived;
            bluetooth1.DeviceInquired += bluetooth1_DeviceInquired;

            button2.ButtonPressed += button2_ButtonPressed;
            bluetooth2.BluetoothStateChanged += bluetooth2_BluetoothStateChanged;
            bluetooth2.DataReceived += bluetooth2_DataReceived;
            bluetooth2.DeviceInquired += bluetooth2_DeviceInquired;

            Debug.Print("Ready to inquire the device");
        }

        void button1_ButtonPressed(Button sender, Button.ButtonState state)
        {
            bluetooth1.HostMode.InquireDevice();
        }

        void bluetooth1_BluetoothStateChanged(Bluetooth sender, Bluetooth.BluetoothState btState)
        {
            if (btState == Bluetooth.BluetoothState.Connected)
            {
                Debug.Print("Bluetooth Connected");
                if (!hasSeeedConnected)
                {
                    hasSeeedConnected = true;
                }
            }
            else
            {
                Debug.Print("Bluetooth Not Connected");
                if (hasSeeedConnected)
                {
                    this.Alarm();

                    if (wifi_RS21.IsNetworkConnected && restClient.NotifyMissingDevice("2066", "1093", "5456")) 
                    {
                        Debug.Print("Missing device notification successful");
                    }
                    else
                    {
                        Debug.Print("Missing device notification failed");
                    }

                    hasSeeedConnected = false;
                }
            }
        }

        void bluetooth1_DeviceInquired(Bluetooth sender, string macAddress, string name)
        {
            if (name.IndexOf("2066") > -1) 
            {
                Debug.Print("Connecting to: " + name);
                bluetooth1.HostMode.Connect(macAddress);
            }
        }

        void bluetooth1_DataReceived(Bluetooth sender, string data)
        {
            Debug.Print("Received: " + data);
        }

        void button2_ButtonPressed(Button sender, Button.ButtonState state)
        {
            bluetooth2.HostMode.InquireDevice();
        }

        void bluetooth2_BluetoothStateChanged(Bluetooth sender, Bluetooth.BluetoothState btState)
        {
            if (btState == Bluetooth.BluetoothState.Connected)
            {
                Debug.Print("Bluetooth Connected");
                if (!hasNexusConnected)
                {
                    hasNexusConnected = true;
                }
            }
            else
            {
                Debug.Print("Bluetooth Not Connected");
                if (hasNexusConnected)
                {
                    this.Alarm();

                    if (wifi_RS21.IsNetworkConnected && restClient.NotifyMissingDevice("2089", "1093", "5454"))
                    {
                        Debug.Print("Missing device notification successful");
                    }
                    else
                    {
                        Debug.Print("Missing device notification failed");
                    }

                    hasNexusConnected = false;
                }
            }
        }

        void bluetooth2_DeviceInquired(Bluetooth sender, string macAddress, string name)
        {
            if (name.IndexOf("2089") > -1) // To be changed
            {
                Debug.Print("Connecting to: " + name);
                bluetooth2.HostMode.Connect(macAddress);
            }
        }

        void bluetooth2_DataReceived(Bluetooth sender, string data)
        {
            Debug.Print("Received: " + data);
        }

        void Alarm()
        {
            // Alarm Sound
            for (int i = 0; i < NOTE_SIZE; ++i)
            {
                PlayNote(Tunes.Tone.C4, 200);
                PlayNote(Tunes.Tone.G4, 200);
            }

            tunes.Play();
        }

        void PlayNote(Tunes.Tone tone, int duration)
        {
            Tunes.MusicNote note = new Tunes.MusicNote(tone, duration);
            tunes.AddNote(note);
        }
    }
}
